package com.core.service.User;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.core.mapper.ReservationMapper;
import com.core.mapper.UserMapper;
import com.core.pojo.Result.Result;
import com.core.pojo.User.dto.UserInfoDTO;
import com.core.pojo.User.dto.UserLoginReqDTO;
import com.core.pojo.User.entity.User;
import com.core.toolkit.PasswordEncoder;
import com.core.toolkit.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.core.constant.LockConstants.LOCK_USER_REGISTER;
import static com.core.constant.RedisConstants.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class UserLoginServiceImpl implements UserLoginService {

    private final UserMapper userMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final RedissonClient redissonClient;
    private final ReservationMapper reservationMapper;

    /**
     * 登录校验
     * @param userLoginReqDTO
     * @return
     */
    @Override
    public Result<String> loginAuthenticate(UserLoginReqDTO userLoginReqDTO) {
        // 1.校验学号格式（12位）
        Long studentId = userLoginReqDTO.getId();
        if (Objects.isNull(studentId) || studentId.toString().length() != 12) {
            return Result.error("学号格式错误：必须为12位数字");
        }
        // 2. 查询用户并验证密码
        User user = queryUserById(userLoginReqDTO.getId());
        if (user == null||user.getId()==null) {
            return Result.error("用户不存在");
        }
        if (!PasswordEncoder.matches(user.getPassword(),userLoginReqDTO.getPassword())) {
            return Result.error("密码错误");
        }
        // 3. 转换为DTO并生成Token
        UserInfoDTO userInfoDTO = new UserInfoDTO();
        BeanUtils.copyProperties(user,userInfoDTO);
        String token = JwtUtil.generateToken(userInfoDTO);
        // 4.刷新redis有效期
        String tokenKey=LOGIN_USER_KEY+userLoginReqDTO.getId();
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL, LOGIN_USER_TIMEUNIT);
        // 5. 返回给前端的结果为Result.success(token)
        return Result.success(token);
    }

    /**
     * 缓存优先策略查询用户信息(无锁的方案）
     */
    public User queryUserById(Long id) {
        // 1. 从Redis查询用户 (核心改动点)
        String tokenKey=LOGIN_USER_KEY+id;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);

        User user = null;
        if (!userMap.isEmpty()) {
            // 2.1.缓存命中：Map转JavaBean
            user = BeanUtil.mapToBean(userMap, User.class, false, CopyOptions.create());
        } else {
            // 2.2.缓存未命中：查数据库
            user = userMapper.selectById(id);
            if (user == null) {
                // 防穿透：缓存空值 (5分钟)
                stringRedisTemplate.opsForHash().put(tokenKey, "__null__", "true");
                stringRedisTemplate.expire(tokenKey, 5, TimeUnit.MINUTES);
                return null;
            }
            // 写入缓存
            Map<String, Object> cacheMap = BeanUtil.beanToMap(
                    user,
                    new HashMap<>(),
                    CopyOptions.create().setIgnoreNullValue(true)
                            .setFieldValueEditor((f,v) -> v.toString())
            );
            stringRedisTemplate.opsForHash().putAll(tokenKey, cacheMap);
            // 设置缓存时间
            stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL, LOGIN_USER_TIMEUNIT);
        }
        return user;
    }


    /**
     * 刷新用户Redis有效期
     */
    @Override
    public void refreshUserTokenTtl(Long id) {
        String tokenKey = LOGIN_USER_KEY + id;
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
    }

    /**
     * 用户注册
     * @param user
     * @return
     */
    @Override
    public Result<User> register(User user) {
        // 1.验证参数是否合理
        Result authenticate=userAuthenticate(user);
        if(authenticate.getCode()!=1){
            return authenticate;
        }
        // 2.加锁开始进行用户注册
        RLock lock = redissonClient.getLock(LOCK_USER_REGISTER + user.getId());
        boolean tryLock = lock.tryLock();
        if(!tryLock){
            // 已经有其它线程在进行注册的情况
            // return Result.error("请求过于频繁，请稍后再试");
            throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS, "请求过于频繁，请稍后再试");
        }
        try {
            // 3.开始执行注册业务
            // 3.1.检查该用户名是否存在
            Boolean absent = stringRedisTemplate.opsForSet().isMember(REGISTER_USER_ID_KEY,user.getId().toString());
            if (Boolean.TRUE.equals(absent)) {
                return Result.error("该用户名已存在");
            }
            // 加密密码
            user.setPassword(PasswordEncoder.encode(user.getPassword()));
            // 3.2.写入数据库
            userMapper.insert(user);
            // 3.3.清理可能的空值缓存（防穿透标记）
            String tokenKey=LOGIN_USER_KEY+user.getId();
            stringRedisTemplate.delete(tokenKey);
            // 3.4.写入redis
            Map<String, Object> cacheMap = BeanUtil.beanToMap(
                    user,
                    new HashMap<>(),
                    CopyOptions.create().setIgnoreNullValue(true)
                            .setFieldValueEditor((f,v) -> v.toString())
            );
            stringRedisTemplate.opsForHash().putAll(tokenKey, cacheMap);
            // 设置缓存时间
            stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL, LOGIN_USER_TIMEUNIT);
            // 把用户名写入set
            stringRedisTemplate.opsForSet().add(REGISTER_USER_ID_KEY,user.getId().toString());
        } finally {
            lock.unlock();
        }
        return Result.success(user);
    }



    /**
     *用户参数验证
     */
    @Override
    public Result userAuthenticate(User user){
        if (Objects.isNull(user.getId())||user.getId()>999999999999L||user.getId()<100000000000L){
            return Result.error("用户id格式错误！");
        }
        if (Objects.isNull(user.getName())){
            return Result.error("用户姓名不能为空！");
        }
        if (Objects.isNull(user.getPassword())){
            return Result.error("用户密码不能为空！");
        }
        String regex = "^1[3-9]\\d{9}$";
        if(Objects.isNull(user.getPhone())||!user.getPhone().matches(regex)){
            return Result.error("用户手机号码格式错误！");
        }
        return Result.success();
    }

    /**
     * 用户退出登录
     * @param accessToken
     */
    @Override
    public void logout(String accessToken) {
        stringRedisTemplate.opsForSet().add(USER_LOGOUT_TOKEN_KEY,accessToken);

    }






}
